#pragma once

#include "Object/Object.h"
#include "Math/Vec3.h"
#include "PhysicalScene.m.h"

class RRigidBody;
class RCollider;

class RBoxCollider;
class RSphereCollider;

//物理世界
RCLASS()
class WHENGINE_API RPhysicalScene : public RObject
{
    ROBJECT

public:
    void RegisterRigidBody(const TSharedObjectPtr<RRigidBody>& PhysicalBody)
    {
        RigidBodies.Add(PhysicalBody);
    }

    void UnregisterRigidBody(const TSharedObjectPtr<RRigidBody>& PhysicalBody)
    {
        RigidBodies.Remove(PhysicalBody);
    }

    void RegisterCollider(const TSharedObjectPtr<RCollider>& InCollider)
    {
        Colliders.Add(InCollider);
    }

    void UnregisterCollider(const TSharedObjectPtr<RCollider>& InCollider)
    {
        Colliders.Remove(InCollider);
    }
protected:
    //刚体,用于描述物体的物理属性,可以受力
    TVector<TSharedObjectPtr<RRigidBody>> RigidBodies;

    //碰撞体，仅用于碰撞检测
    TVector<TSharedObjectPtr<RCollider>> Colliders;

public:
    void Tick(float DeltaTime);

    //设置物理世界的时间步长
    //单位为秒
    void SetTickPeriod(float InTickPeriod)
    {
        TickPeriod = InTickPeriod;
    }

    float GetTickPeriod()
    {
        return TickPeriod;
    }
protected:

    //物理世界的时间步长，单位为秒
    float TickPeriod= 1.0f / 30.0f;

    //距离上一次物理更新的时间
    float AccumulatedTime = 0.0f;

    void TickOneStep(float DeltaTime);

    //更新所有刚体的状态
    void UpdateAllRigidBodyState(float DeltaTime);

    //更新刚体的状态
    void UpdateRigidBodyState(TSharedObjectPtr<RRigidBody> InBody,float DeltaTime);

    //粗粒度碰撞检测
    void BroadPhaseCollisionDetection();
    //粗粒度碰撞检测结果
    TVector<TVector<int32_t> > BroadPhaseResult;

    //细粒度碰撞检测
    void NarrowPhaseCollisionDetection();
    //细粒度碰撞检测结果
    struct SCollisionResult
    {
        TSharedObjectPtr<RCollider> ColliderA;
        TSharedObjectPtr<RCollider> ColliderB;

        //碰撞点
        SVec3 ContactPoint;
        //碰撞法线
        SVec3 Normal;
        //插入深度
        float Penetration;
    };

    TVector<SCollisionResult> NarrowPhaseResult;

    //两个碰撞体在业务上是否可以碰撞
    bool NeedCollisionDetect(TSharedObjectPtr<RCollider> ColliderA, TSharedObjectPtr<RCollider> ColliderB);

    //依据碰撞体的形状进行碰撞检测
    TOptional<SCollisionResult> CollisionDetect(
        TSharedObjectPtr<RCollider> ColliderA
        , TSharedObjectPtr<RCollider> ColliderB);

    TOptional<SCollisionResult> CollisionDetectSphereSphere(
        TSharedObjectPtr<RSphereCollider> SphereA
        , TSharedObjectPtr<RSphereCollider> SphereB);
    
    TOptional<SCollisionResult> CollisionDetectSphereBox(
        TSharedObjectPtr<RSphereCollider> Sphere
        , TSharedObjectPtr<RBoxCollider> Box);

    TOptional<SCollisionResult> CollisionDetectBoxBox(
        TSharedObjectPtr<RBoxCollider> BoxA
        , TSharedObjectPtr<RBoxCollider> BoxB);

    //处理碰撞
    //当发生碰撞后,需要将碰撞体的状态进行调整
    //并且需要将碰撞体的碰撞事件进行分发
    void HandleCollisionResult();

    //两个碰撞体在业务上是否可以重叠
    //如果不允许重叠，那么碰撞体在碰撞时会被推开
    bool AllowOverlap(TSharedObjectPtr<RCollider> ColliderA, TSharedObjectPtr<RCollider> ColliderB);

    //获取碰撞体对应的刚体
    TSharedObjectPtr<RRigidBody> GetRigidBodyByCollider(TSharedObjectPtr<RCollider> InCollider);

};